#include<iostream>
using namespace std;
#include<pthread.h>
#include<vector>
#include<unistd.h>



// 现在使用锁


// 3. 这里甚至可以用c++11的mutex库
// 4. 或者是自己封装的Mutex库
static int ticket = 1000;

#include<mutex>
#include"Mutex.hpp"

class TaskData{
public:
    /* TaskData(mutex& lock, const string& name = "thread_default")
        :_plock(&lock),
        _name(name)
    {} */

    TaskData(myMutex::Mutex& lock, const string& name = "thread_default")
        :_plock(&lock),
        _name(name)
    {}

    ~TaskData(){}

    /* mutex* Get_Mutex_Pointer(){
        return _plock;
    }   */

    myMutex::Mutex* Get_Mutex_Pointer(){
        return _plock;
    }  

    myMutex::Mutex& Get_Mutex() const {
        return *_plock;
    }

    const string& GetName() const{
        return _name;
    } 
    
private:
    //还可以设置一些其它的参数使用... 这里就不展示了

    // 为了让所有线程都用同一把锁，所以需要这样子传入指针给每个线程用
    myMutex::Mutex* _plock;
    string _name;
};

/* void* GetTicket(void* args){
    TaskData* ptd = static_cast<TaskData*>(args);
    // 不断执行

    while(1){
        // 这一次，把沉睡时间放在这里就可以使得每个线程抢票比较均匀
        usleep(1000);
        // 加锁
        //ptd->Get_Mutex_Pointer()->lock();  //3.

        //ptd->Get_Mutex_Pointer()->Lock();  //4.

        // 5. 通过RAII思想来进行加锁解锁
        myMutex::Lock_Guard(ptd->Get_Mutex());
        // 每次while循环开始自动创建锁，加锁
        // 单次while循环结束，就自动解锁，删除锁



        if(ticket > 0){
            // 放在里面就很可能导致单一线程抢票
            // usleep(1000);
            cout << ptd->GetName() << " : 开始抢票, 当前票余量为"<< ticket << endl;
            ticket--;
            // 解锁
            // ptd->Get_Mutex_Pointer()->unlock(); //3.
            // ptd->Get_Mutex_Pointer()->UnLock(); //4.
        }
        else{
            // 解锁
            // ptd->Get_Mutex_Pointer()->unlock(); //3.
            // ptd->Get_Mutex_Pointer()->UnLock(); //4.
            break;
        }
    }
    // 先不关心返回值
    return nullptr;
} */

/* int main(){
    //mutex lock; //初始化了
    myMutex::Mutex lock;

    TaskData* pTaskData1 = new TaskData(lock, "thread_1");
    TaskData* pTaskData2 = new TaskData(lock, "thread_2");
    TaskData* pTaskData3 = new TaskData(lock, "thread_3");
    TaskData* pTaskData4 = new TaskData(lock, "thread_4");

    pthread_t tid1, tid2, tid3, tid4;
    pthread_create(&tid1, nullptr, GetTicket, pTaskData1);
    pthread_create(&tid2, nullptr, GetTicket, pTaskData2);
    pthread_create(&tid3, nullptr, GetTicket, pTaskData3);
    pthread_create(&tid4, nullptr, GetTicket, pTaskData4);

    pthread_join(tid1, nullptr);
    pthread_join(tid2, nullptr);
    pthread_join(tid3, nullptr);
    pthread_join(tid4, nullptr);

    return 0;

} */








/* class TaskData{
public:
    TaskData(pthread_mutex_t& lock, const string& name = "thread_default")
        :_plock(&lock),
        _name(name)
    {}
    ~TaskData(){}

    pthread_mutex_t* GetPthread_mutex_addr() const{
        return _plock;
    }

    const string& GetName() const{
        return _name;
    } 
    
private:
    //还可以设置一些其它的参数使用... 这里就不展示了

    // 为了让所有线程都用同一把锁，所以需要这样子传入指针给每个线程用
    pthread_mutex_t* _plock;
    string _name;
};

static int ticket = 1000;

void* GetTicket(void* args){
    TaskData* ptd = static_cast<TaskData*>(args);
    // 不断执行

    while(1){
        // 这一次，把沉睡时间放在这里就可以使得每个线程抢票比较均匀
        usleep(1000);
        // 加锁
        pthread_mutex_lock(ptd->GetPthread_mutex_addr());
        if(ticket > 0){
            // 放在里面就很可能导致单一线程抢票
            // usleep(1000);
            cout << ptd->GetName() << " : 开始抢票, 当前票余量为"<< ticket << endl;
            ticket--;
            // 解锁
            pthread_mutex_unlock(ptd->GetPthread_mutex_addr());
        }
        else{
            // 解锁
            pthread_mutex_unlock(ptd->GetPthread_mutex_addr());
            break;
        }
    }
    // 先不关心返回值
    return nullptr;
}



// 2. 局部锁
int main(){
    //这里写的简单粗暴一点
    pthread_mutex_t lock;
    pthread_mutex_init(&lock, nullptr);

    TaskData* pTaskData1 = new TaskData(lock, "thread_1");
    TaskData* pTaskData2 = new TaskData(lock, "thread_2");
    TaskData* pTaskData3 = new TaskData(lock, "thread_3");
    TaskData* pTaskData4 = new TaskData(lock, "thread_4");

    pthread_t tid1, tid2, tid3, tid4;
    pthread_create(&tid1, nullptr, GetTicket, pTaskData1);
    pthread_create(&tid2, nullptr, GetTicket, pTaskData2);
    pthread_create(&tid3, nullptr, GetTicket, pTaskData3);
    pthread_create(&tid4, nullptr, GetTicket, pTaskData4);

    pthread_join(tid1, nullptr);
    pthread_join(tid2, nullptr);
    pthread_join(tid3, nullptr);
    pthread_join(tid4, nullptr);

    pthread_mutex_destroy(&lock);
    return 0;
}  */




// 1. 全局锁  -> 不需要手动销毁锁
/* pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER;

static int ticket = 1000;

void* GetTicket(void* args){
    string name = static_cast<char*>(args);
    // 不断执行

    while(1){
        // 这一次，把沉睡时间放在这里就可以使得每个线程抢票比较均匀
        usleep(1000);
        // 加锁
        pthread_mutex_lock(&global_lock);
        if(ticket > 0){
            // 放在里面就很可能导致单一线程抢票
            // usleep(1000);
            cout << name << " : 开始抢票, 当前票余量为"<< ticket << endl;
            ticket--;
            // 解锁
            pthread_mutex_unlock(&global_lock);
        }
        else{
            // 解锁
            pthread_mutex_unlock(&global_lock);
            break;
        }
    }
    // 先不关心返回值
    return nullptr;
}

#define DEFAULT_THREAD_NUM 4

int main(){
    vector<pthread_t> threads(DEFAULT_THREAD_NUM, 0);
    vector<string> names(DEFAULT_THREAD_NUM, "");
    
    for(int i = 1; i <= DEFAULT_THREAD_NUM; ++i){
        char* id = new char[64]{0};
        snprintf(id, 64, "thread_%d", i);
        names[i - 1] = id;
        


        // 创建线程
        int threadCreate_id = pthread_create(&threads[i - 1], nullptr, GetTicket, (void*)names[i - 1].c_str());
        //if(threadCreate_id == 0){cout << names[i - 1] << "线程创建成功" << endl;}
        if(threadCreate_id == 0){cout << id << "线程创建成功" << endl;}
        //else{cout << names[i - 1] << "线程创建失败" << endl;}   
        else{cout << id << "线程创建失败" << endl;}   
        delete[] id;
    }

    

    for(auto& tid : threads){
        pthread_join(tid, nullptr);
    }

    return 0;
} */



// 使用线程自带的Mutex使用
// 首先需要验证一下数据不一致问题

// 模拟抢票 -> 如果不加锁：
/* static int ticket = 1000;

void* GetTicket(void* args){
    string name = static_cast<char*>(args);
    // 不断执行
    while(1){
        if(ticket > 0){
            usleep(1000);
            cout << name << " : 开始抢票, 当前票余量为"<< ticket << endl;
            ticket--;
        }
        else{
           // cout << name << " : 票余量不足" << endl;
            break;
        }
    }
    // 先不关心返回值
    return nullptr;
}

#define DEFAULT_THREAD_NUM 4

int main(){
    vector<pthread_t> threads(DEFAULT_THREAD_NUM, 0);
    vector<string> names(DEFAULT_THREAD_NUM, "");
    
    for(int i = 1; i <= DEFAULT_THREAD_NUM; ++i){
        char* id = new char[64]{0};
        snprintf(id, 64, "thread_%d", i);
        names[i - 1] = id;
        delete[] id;

        // 创建线程
        int threadCreate_id = pthread_create(&threads[i], nullptr, GetTicket, (void*)names[i - 1].c_str());
        if(threadCreate_id == 0){cout << names[i - 1] << "线程创建成功" << endl;}
        else{cout << names[i - 1] << "线程创建失败" << endl;}      
        
        
    }

    

    for(auto& tid : threads){
        pthread_join(tid, nullptr);
    }

    return 0;
} */